/*
 * Copyright (c) 1999-2001
 * Information Promotion Agency of Japan (IPA),
 * Fujitsu Limited, and 
 * Matsuoka Laboratory, Tokyo Institute of Technology
 * All rights reserved.
 *
 * Redistribution and non-commercial use in source and binary forms, 
 * with or without modification, are permitted provided that the following 
 * conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * 3. All advertising materials mentioning features or use of this 
 *    software must display the following acknowledgement:
 *
 *    This product includes software developed by Information Promotion Agency
 *    of Japan, Fujitsu Limited, and Matsuoka Laboratory, 
 *    Tokyo Institute of Technology.
 *
 * 4. The names "Information Promotion Agency", "Fujitsu Limited",
 *    "Matsuoka Laboratory", or "Tokyo Institute of Technology" should
 *    not be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 *
 * 5. Any use of the source code or the binary in a commercial product, 
 *    whether may it be the origial representation or in some modified form,
 *    is not permitted without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * $Revision: 1.18 $
 * $Date: 2000/09/26 04:27:29 $
 * $Author: kouya $
 */

package org.OpenJIT;

class SubrNode {
    SubrNode next;
    int pc;
    int entry_stack_size;
    int ret_stack_size;

    public SubrNode(int pc) {
	this.pc = pc;
    }
}

class SubrNodeList {
    SubrNode subr;

    public void append(SubrNode subr) {
	subr.next = this.subr;
	this.subr = subr;
    }

    public SubrNode find(int pc) {
	SubrNode subr = this.subr;
	while(subr != null) {
	    if (subr.pc == pc)
		return subr;
	    subr = subr.next;
	}
	return null;
    }
}

public abstract class ConvertRTL extends ParseBytecode {

    private final void convBB(int pc, byte stackTag[], SubrNodeList subrList, SubrNode entry) {
	BCinfo bc = bcinfo[pc];
	int cur_stack_size;
	int new_stack_size;

    loop:
	while(true) {
	    if ((debug & DEBUG_RTL) != 0)
		System.err.println("*** BEGIN BASIC BLOCK:" + pc);

	    bc = bcinfo[pc];
	    new_stack_size = bc.stack_size;

	    for (; pc < bytecode_length; pc++) {
		bc = bcinfo[pc];
		if (bc == null)
		    continue;

		if ((bc.flag & BCinfo.FLAG_CTRLFLOW_DONE) != 0)
		    return;

		bc.flag |= BCinfo.FLAG_CTRLFLOW_DONE;

		cur_stack_size = new_stack_size;
		new_stack_size = cur_stack_size + bc.stack_delta;
		bc.stack_size = cur_stack_size;

		if ((debug & DEBUG_RTL) != 0)
		    System.err.println(pc + "\t" + cur_stack_size + 
				       "->" + new_stack_size +
				       "\t" + opcName(pc));

		for (ILnode il = bcinfo[pc].next; il != null; il = il.next) {
		    int op = il.op();
		    if (op == ILnode.BRANCH) {
			int cond = il.lval;
			int target_pc = il.rval;
			BCinfo target_bc = bcinfo[target_pc];

			if ((debug & DEBUG_RTL) != 0)
			    System.err.println("*** BRANCH HAS TARGET " + target_pc);

			if (cond == CC_A) { // absolute branch
			    if ((target_bc.flag & BCinfo.FLAG_CTRLFLOW_DONE) == 0) {
				target_bc.stack_size = new_stack_size;
				pc = target_pc;
				continue loop;
			    }
			    return;
			}
			if (cond == CC_JSR) { // subroutine call

			    if ((target_bc.flag & BCinfo.FLAG_CTRLFLOW_DONE) == 0) {
				// verification
				if (target_bc.next != null &&
				    target_bc.next.op() != ILnode.MOVE &&
				    target_bc.next.rval != -1)
				    throw new CompilerError("subroutine doesn't begin astore");

				// jsr pushes PC on stack
				SubrNode subr = new SubrNode(target_pc);
				stackTag[new_stack_size] = (byte)TAG_PI;
				target_bc.stack_size = new_stack_size + 1;
				subr.entry_stack_size = new_stack_size;
				subr.ret_stack_size = -1;
				if ((debug & DEBUG_RTL) != 0)
				    System.err.println("*** BEGIN SUBROUTINE");
				convBB(target_pc, stackTag, subrList, subr);
				if ((debug & DEBUG_RTL) != 0)
				    System.err.println("*** END OF SUBROUTINE");
				if (subr.ret_stack_size == -1) {
				    // this subroutine is never ended by "RET"
				    // FIX ME: should raise an Error
				    subr.ret_stack_size = 0;
				}
				new_stack_size = subr.ret_stack_size;
				target_bc.flag |= BCinfo.FLAG_SUBROUTINE_ENTRY;
				subrList.append(subr);
			    } else {
				if ((target_bc.flag & BCinfo.FLAG_SUBROUTINE_ENTRY) == 0)
				    throw new CompilerError("Multiple subroutine entry?");
				SubrNode subr = subrList.find(target_pc);
				if (subr.entry_stack_size != new_stack_size)
				    throw new CompilerError("Inconsistent stack size at subroutine entry point");
				// FIX ME: if a subroutine pushes something on stack, 
				// stackTag might be wrong.
				new_stack_size = subr.ret_stack_size;
			    }
			} else if ((target_bc.flag & BCinfo.FLAG_CTRLFLOW_DONE) == 0) {
			    target_bc.stack_size = new_stack_size;

			    byte newStackTag[] = new byte [stackTag.length];
			    System.arraycopy(stackTag, 0, newStackTag, 0, new_stack_size);
			    convBB(target_pc, newStackTag, subrList, entry);
			}
		    } else if (op == ILnode.MOVE) {
			if (il.lval == -1) {
			    // determin unknown stack type as dup,swap,...etc
			    int i = il.rval + cur_stack_size;
			    il.lval = 0;
			    // Is this insn a part of double word ?
			    if (i > 0 && (stackTag[i-1] & TYPE_MASK) == TYPE_DOUBLE) {
				il.remove();
				// skip to next insn
				continue;
			    }
			    byte tag = (byte)(TAG_STACK | (stackTag[i] & TYPE_MASK));
			    il.move(tag, il.dval, tag, il.rval);
			}
		    } else if (op == ILnode.RET) {
			if (entry == null)
			    throw new CompilerError("Detect ret operation without jsr");
			entry.ret_stack_size = new_stack_size;
		    }

		    if (il.tagL() == TAG_STACK)
			il.lval += cur_stack_size + nlocals;

		    if (il.tagR() == TAG_STACK)
			il.rval += cur_stack_size + nlocals;

		    if (il.tagD() == TAG_STACK) {
			if (!il.isSTORE()) {
			    stackTag[il.dval + cur_stack_size] = (byte)il.dtype();
			}
			il.dval += cur_stack_size + nlocals;
		    }
		    if ((debug & DEBUG_RTL) != 0)
			System.err.println("***\t\t\t"+il);
		}
		if ((bc.flag & BCinfo.FLAG_CTRLFLOW_END) != 0)
		    return;
	    }
	    break;
	}
    }

    final void convertRTL() throws CompilerError {
	BCinfo bc;
	byte stackTag[];

	stackTag = new byte [maxstack + 1];

	// setup for entry point
	bc = bcinfo[0];
	bc.stack_size = 0;

	// setup for exception table
	for (int i = 0; i < exceptionHandler.length; i++) {
	    ExceptionHandler x = exceptionHandler[i];
	    bc = bcinfo[x.handlerPC];
	    bc.flag |= BCinfo.FLAG_BRANCH_TARGET | BCinfo.FLAG_EXCEPTION_HANDLER;
	    bc.stack_size = 1;
	    ILnode il = (new ILnode()).move(TAG_SI,(-1), TAG_PI,(ARG0));
	    il.next = bc.next;
	    bc.next = il;
	    for (int pc = x.startPC; pc <= x.endPC; pc++) {
		bc = bcinfo[pc];
		if (bc != null) {
		    bc.flag |= BCinfo.FLAG_IN_TRY_BLOCK;
		}
	    }
	}

	SubrNodeList subrList = new SubrNodeList();

	convBB(0, stackTag, subrList, null);

	for (int i = 0; i < exceptionHandler.length; i++) {
	    stackTag[0] = (byte)TAG_SI;
	    convBB(exceptionHandler[i].handlerPC, stackTag, subrList, null);
	}
    }

    static {
	// force resolving SubrNode Class to prevent from the
	// duplicate class definition.
	new SubrNode(0);
    }
}
